﻿using Gui.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;

namespace Gui.Scan
{
    class CodeScanner
    {
        public delegate void ScanerDelegate(string codes);
        public event ScanerDelegate ValidScanCodeEvent;
        public event ScanerDelegate InValidScanCodeEvent;

        delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        private HookProc hookproc;
        private int hKeyboardHook = 0;

        private int span = 30; // 指定输入间隔为30毫秒以内时为连续输入
        private int debounce = 120;//触发扫描成功事件的防抖间隔时间
        private List<char> charList = new List<char>();  // 扫描到的标识码
        private int lastCharScanTime; //上一字符扫描时间
        private byte[] keyBoardState = new byte[256];

        //事件防抖
        private Timer validScanTimer;


        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);
        [DllImport("user32", EntryPoint = "GetKeyNameText")]
        private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize);
        [DllImport("user32", EntryPoint = "GetKeyboardState")]
        private static extern int GetKeyboardState(byte[] pbKeyState);
        [DllImport("user32", EntryPoint = "ToAscii")]
        private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        public bool Start()
        {
            if (hKeyboardHook == 0)
            {
                hookproc = new HookProc(KeyboardHookProc);
                //防止在 framework4.0中 注册钩子不成功
                IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
                hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0);
            }
            return hKeyboardHook != 0;
        }
        public bool Stop()
        {
            if (hKeyboardHook != 0)
            {
                return UnhookWindowsHookEx(hKeyboardHook);
            }
            return true;
        }

        private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (wParam != 0x100)
            {
                return 0;
            }
            EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
            uint uKey = 0;

            GetKeyboardState(keyBoardState);
            bool suc = ToAscii(msg.message, msg.paramL, keyBoardState, ref uKey, 0);
            var ch = Convert.ToChar(uKey);
            if (suc)
            {
                addChar(ch, msg.time);
            }
            return 0;
        }

        void addChar(char ch, int msgTime)
        {
            if (msgTime - lastCharScanTime > span)
            {
                charList.Clear();
            }
            charList.Add(ch);
            lastCharScanTime = msgTime;

            ScanTrigger();
        }


        public void ScanTrigger()
        {
            if (validScanTimer == null)
            {
                validScanTimer = new Timer(debounce);
                validScanTimer.AutoReset = false;
                validScanTimer.Elapsed += (o, e) =>
                {
                    validScanTimer.Stop();
                    validScanTimer.Close();
                    validScanTimer = null;

                    string code = new string(charList.ToArray());
                    if (CodeChecker.IsValidCode(code))
                    {
                        Console.WriteLine("合法" + code);
                        if (ValidScanCodeEvent != null)
                            ValidScanCodeEvent(code);
                    }
                    else
                    {
                        Console.WriteLine("非法" + code);
                        if (InValidScanCodeEvent != null && code.Length > 2)
                            InValidScanCodeEvent(code);
                    }
                    charList.Clear();
                };
            }
            validScanTimer.Stop();
            validScanTimer.Start();
        }


        public struct EventMsg
        {
            public int message;
            public int paramL;
            public int paramH;
            public int time;
            public int hwnd;
        }
    }
}